﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Xml.Linq;
using Microsoft.AnalysisServices.AdomdClient;
using Microsoft.SqlServer.Management.Trace;
using System.Security.Cryptography;
using System.ComponentModel;
using System.Windows;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using System.Windows.Input;
using Microsoft.Win32;
using System.Xml;

namespace StressTesterCommon
{
    public class RelayCommand : ICommand
    {
        private Action _handler;
        public RelayCommand(Action handler)
        {
            _handler = handler;
        }

        private bool _isEnabled;
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                if (value != _isEnabled)
                {
                    _isEnabled = value;
                    if (CanExecuteChanged != null)
                    {
                        CanExecuteChanged(this, EventArgs.Empty);
                    }
                }
            }
        }

        public bool CanExecute(object parameter)
        {
            return IsEnabled;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            _handler();
        }
    }
    /*
    public class MTObservableCollection<T> : ObservableCollection<T>
    {
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            var eh = CollectionChanged;
            if (eh != null)
            {
                Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()
                                         let dpo = nh.Target as DispatcherObject
                                         where dpo != null
                                         select dpo.Dispatcher).FirstOrDefault();

                if (dispatcher != null && dispatcher.CheckAccess() == false)
                {
                    dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e)));
                }
                else
                {
                    foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList())
                        nh.Invoke(this, e);
                }
            }
        }
    }
     * */
    public enum QueryExecuteStatus
    {
        QueryFailed = 0,
        QuerySucceeded = 1
    }
    public enum MessageTypes
    {
        ClientProvideIdData = 1,
        ServerMessage = 2,
        TestConfiguration = 3,
        TestConnection = 4,
        TestConfigurationOk = 5,
        TestConfigurationFailed = 6,
        TestConnectionOk = 7,
        TestConnectionFailed = 8,
        ExecuteTest = 9,
        QueryResult = 10,
        TestCompleted = 11,
        ClientDisconnect = 12,
        ClientAbortTest = 13,
        HeartBeat = 14,
        ClientTestStarted = 15,
        QueryResultSet = 16
    }

    public enum QueryPickMode
    {
        Sequential = 1,
        Random = 2
    }

    public enum ConnectionType
    {
        SSAS = 1,
        SQLServerDb = 2
    }

    public abstract class  QuerySource
    {
        private Test _Parent;

        public Test Parent
        {
            get { return _Parent; }
            set { _Parent = value; }
        }
        public QuerySource(Test Parent)
        {
            _Parent = Parent;
        }

        public List<Query> GetQueries(XElement Elem)
        {
            List<Query> Ret = new List<Query>();
            foreach (XElement E in Elem.Elements("query"))
            {
                Query Q = new Query();
                Q.FromXelement(E);
                Ret.Add(Q);                
            }
            return Ret;
        }
        public abstract List<Query> GetQueries();
        public abstract XElement ToXElement();
        public abstract void FromXElement(XElement Elem);
    }

    public class QuerySourceNative:QuerySource
    {
        public QuerySourceNative(Test Parent) : base(Parent)
        {
            
        }
        public override XElement ToXElement()
        {
            XElement Ret = new XElement("querysource");
            XElement Elem = new XElement("type");
            Elem.Value = "native";
            Ret.Add(Elem);
            return Ret;
        }
        public override void FromXElement(XElement Elem)
        {

        }
        public override List<Query> GetQueries()
        {
            List<Query> Ret = new List<Query>();
            Query q;
            XDocument Doc = XDocument.Load(Parent.FilePath);
            foreach (XElement Elem in Doc.Element("test").Element("queries").Elements("query"))
            {
                q = new Query();
                q.Name = Elem.Element("name").Value;
                q.Text = Elem.Element("querytext").Value;
                Ret.Add(q);
            }
            return Ret;
        }
    }
    public class QuerySourceTraceFile : QuerySource
    {
        private bool _UniqueQueriesOnly = true;

        public bool UniqueQueriesOnly
        {
            get { return _UniqueQueriesOnly; }
            set { _UniqueQueriesOnly = value; OnPropertyChanged("UniqueQueriesOnly"); }
        }
        private int _TopNQueries = -1;

        public int TopNQueries
        {
            get { return _TopNQueries; }
            set { _TopNQueries = value; }
        }

        private string _TraceFilePath = "";

        public string TraceFilePath
        {
            get { return _TraceFilePath; }
            set { _TraceFilePath = value; OnPropertyChanged("TraceFilePath"); OnPropertyChanged("TraceFileName"); }
        }

        public string TraceFileName
        {
            get { return System.IO.Path.GetFileName(_TraceFilePath); }
        }
        public QuerySourceTraceFile(Test Parent)
            : base(Parent)
        {
            
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string info)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(info));
            }
        }
        public override List<Query> GetQueries()
        {
            List<Query> Ret = new List<Query>();
            if (_UniqueQueriesOnly)
            {
                Ret = GetUniqueQueries();
            }
            else
            {
                Ret = GetAllQueries();
            }
            
            return Ret;
        }
        public override XElement ToXElement()
        {
            XElement Ret = new XElement("querysource");
            XElement Elem = new XElement("type");
            Elem.Value = "trace";
            Ret.Add(Elem);
             Elem = new XElement("filepath");
            Elem.Value = _TraceFilePath;
            Ret.Add(Elem);
            Elem = new XElement("uniquequeries");
            Elem.Value = _UniqueQueriesOnly ? "1" : "0";
            Ret.Add(Elem);
            Elem = new XElement("count");
            Elem.Value = _TopNQueries.ToString();
            Ret.Add(Elem);
            return Ret;          
        }

        public override void FromXElement(XElement Elem)
        {
            _TraceFilePath = Elem.Element("filepath").Value;
            _TopNQueries = Int32.Parse(Elem.Element("count").Value);
            _UniqueQueriesOnly = Elem.Element("uniquequeries").Value=="0"?false:true;
        }

        private int GetDabaseColumnPos(TraceFile File)
        {
            for (int i = 0; i <= File.FieldCount; i++)
            {
                if (File.GetName(i).ToUpper() == "DATABASENAME")
                {
                    return i;
                }
            }
            return -1;
        }

        private int GetErrorColumnPos(TraceFile File)
        {
            for (int i = 0; i <= File.FieldCount; i++)
            {
                if (File.GetName(i).ToUpper() == "ERROR")
                {
                    return i;
                }
            }
            return -1;
        }
        private int GetEventClassColumnPos(TraceFile File)
        {
            for (int i = 0; i <= File.FieldCount; i++)
            {
                if (File.GetName(i).ToUpper() == "EVENTCLASS")
                {
                    return i;
                }
            }
            return -1;
        }

        private int GetTextDataColumnPos(TraceFile File)
        {
            for (int i = 0; i <= File.FieldCount; i++)
            {
                if (File.GetName(i).ToUpper() == "TEXTDATA")
                {
                    return i;
                }
            }
            return -1;
        }

        private List<Query> GetAllQueries()
        {
            if (_TraceFilePath == "")
            {
                return new List<Query>();
            }
            try
            {
                List<Query> Ret = new List<Query>();
                TraceFile File = new TraceFile();
                File.InitializeAsReader(_TraceFilePath);
                int IndexDb = GetDabaseColumnPos(File);
                int IndexClass = GetEventClassColumnPos(File);
                int IndexTextData = GetTextDataColumnPos(File);
                int IndexError = GetErrorColumnPos(File);
                int Count = 1;
                while (File.Read())
                {
                    if (File.GetString(IndexClass).ToUpper() == "QUERY END")
                    {
                        if (Parent.Connection.Database.ToUpper() == File.GetString(IndexDb).ToUpper() && File.IsNull(IndexError))
                        {
                            Query Q = new Query();
                            Q.Text = File.GetString(IndexTextData);
                            Q.Name = "Trace query " + Count.ToString();
                            Ret.Add(Q);
                            Count++;
                        }
                    }

                }
                return Ret;
            }
            catch
            {
                throw;
            }



        }
        private List<Query> GetUniqueQueries()
        {
            if (_TraceFilePath == "")
            {
                return new List<Query>();
            }
            List<Query> Ret = new List<Query>();
            Dictionary<string, Query> UniqueQueries = new Dictionary<string,Query>();
            Dictionary<string, int> QueryCount = new Dictionary<string, int>();
            TraceFile File = new TraceFile();
            File.InitializeAsReader(_TraceFilePath);
            int IndexDb = GetDabaseColumnPos(File);
            int IndexClass = GetEventClassColumnPos(File);
            int IndexTextData = GetTextDataColumnPos(File);
            int IndexError = GetErrorColumnPos(File);
            int Count = 1;
            int NameCount = 1;
            while (File.Read())
            {
                if (File.GetString(IndexClass).ToUpper() == "QUERY END" && File.IsNull(IndexError))
                {
                    if (Parent.Connection.Database.ToUpper() == File.GetString(IndexDb).ToUpper())
                    {
                        Query Q = new Query();
                        Q.Text = File.GetString(IndexTextData);
                        Q.Name = "Trace query " + NameCount.ToString();
                        
                        if (!UniqueQueries.ContainsKey(Q.Hash))
                        {
                            UniqueQueries.Add(Q.Hash, Q);
                            QueryCount.Add(Q.Hash, 1);
                            NameCount++;
                        }
                        else
                        {
                            QueryCount[Q.Hash]++;
                        }
                    }
                }
                Count++;
            }
            
            //HANDLE TOPN IF SET
            if (_TopNQueries > 0)
            {
                int Counter = 1;
                //Order queries
                foreach (var item in QueryCount.OrderByDescending(Value =>Value.Value)) 
                {
                    if (Counter <= _TopNQueries)
                    {
                        Ret.Add(UniqueQueries[item.Key]);
                        Counter++;
                    }
                    else
                    {
                        break;
                    }
                }
                
            }
            else
            {
                foreach (Query Q in UniqueQueries.Values)
                {
                    Ret.Add(Q);
                }
            }

            return Ret;
            
        }
        

    }

    public class Connection 
    {

        public virtual string ConnectionString
        {
            get { return ""; }
        }
        private ConnectionType _ConnectionType;

        public ConnectionType ConnectionType
        {
            get { return _ConnectionType; }
            set { _ConnectionType = value; }
        }
        private string _Server = "";

        public string Server
        {
            get { return _Server; }
            set { _Server = value;  }
        }
        private string _Database = "";

        public string Database
        {
            get { return _Database; }
            set { _Database = value; }
        }
        
        public void LoadFromXelement(XElement Elem) 
        {
            this.ConnectionType = (ConnectionType) Int32.Parse(Elem.Element("connectiontype").Value);
            this.Server = Elem.Element("server").Value;
            this.Database = Elem.Element("database").Value;
        }

        public XElement ToXElement()
        {
            XElement Ret = new XElement("connection");
            XElement Elem = new XElement("server");
            Elem.Value = _Server;
            Ret.Add(Elem);
            Elem = new XElement("database");
            Elem.Value = _Database;
            Ret.Add(Elem);
            Elem = new XElement("connectiontype");
            int i = (int)_ConnectionType;
            Elem.Value = i.ToString();
            Ret.Add(Elem);
            return Ret;
        }

        public bool Validate()
        {
            if (_Database == null || _Database == "")
            {
                return false;
            }

            if (_Server == null || _Server == "")
            {
                return false;
            }
            return true;
        }
        public virtual bool Connect() {return false;}

        public virtual bool Disconnect() { return false; }

        public virtual bool TestConnection() { return false; }

        public virtual QueryResult ExecuteQuery(Query Query) { return new QueryResult(); }
    }

    public class TCPReader
    {
        public static TCPMessage ReadMessageFromStream(NetworkStream Stream)
        {
            TCPMessage Ret = new TCPMessage();

            //Read length section
            byte[] Buffer = new byte[4];
            int MessageLength = sizeof(int);
            int BytesRecieved = 0;
            while (BytesRecieved < MessageLength)
            {
                BytesRecieved += Stream.Read(Buffer, BytesRecieved, Buffer.Length - BytesRecieved);
            }
            Ret.Length = BitConverter.ToInt32(Buffer, 0);
            //Read messagetype section
            Buffer = new byte[4];
            MessageLength = sizeof(int);
            BytesRecieved = 0;
            while (BytesRecieved < MessageLength)
            {
                BytesRecieved += Stream.Read(Buffer, BytesRecieved, Buffer.Length - BytesRecieved);
            }
            Ret.MessageType = (MessageTypes)BitConverter.ToInt32(Buffer, 0);
            //Read message
            BytesRecieved = 0;
            Buffer = new byte[Ret.Length];
            while (BytesRecieved < Ret.Length)
            {
                BytesRecieved += Stream.Read(Buffer, BytesRecieved, Buffer.Length - BytesRecieved);
            }
            Ret.Message = Encoding.ASCII.GetString(Buffer);
            return Ret;
        }
    }
    public class TCPMessage
    {
        int _Length;

        public int Length
        {
            get { return _Length; }
            set { _Length = value; }
        }
        MessageTypes _MessageType;

        public MessageTypes MessageType
        {
            get { return _MessageType; }
            set { _MessageType = value; }
        }
        string _Message;

        public string Message
        {
            get { return _Message; }
            set { _Message = value; }
        }



        static public byte[] FormatMessage(MessageTypes MessageType, string Message)
        {
            byte[] message = System.Text.Encoding.ASCII.GetBytes(Message);
            byte[] length = BitConverter.GetBytes(message.Length);
            byte[] type = BitConverter.GetBytes((int)MessageType);
            byte[] ret = new byte[length.Length + type.Length + message.Length];
            length.CopyTo(ret, 0);
            type.CopyTo(ret, length.Length);
            message.CopyTo(ret, length.Length + type.Length);        
            return ret;
        }
    }

    public class QueryResult
    {
        #region Members
        private Guid _ClientId;
        public Guid ClientId
        {
            get { return _ClientId; }
            set { _ClientId = value; }
        }
        private int _ErrorId = 0;
        public int ErrorId
        {
            get { return _ErrorId; }
            set { _ErrorId = value; }
        }
        private string _ErrorMessage = "";
        public string ErrorMessage
        {
            get { return _ErrorMessage; }
            set { _ErrorMessage = value; }
        }
        private QueryExecuteStatus _ExecuteStatus;
        public QueryExecuteStatus ExecuteStatus
        {
            get { return _ExecuteStatus; }
            set { _ExecuteStatus = value; }
        }
        private string _QueryName;
        public string QueryName
        {
            get { return _QueryName; }
            set { _QueryName = value; }
        }
        private string _QueryText;
        public string QueryText
        {
            get { return _QueryText; }
            set { _QueryText = value; }
        }
        private double _Duration;
        public double  Duration
        {
            get { return _Duration; }
            set { _Duration = value; }
        }
        private DateTime _QueryStartTime = DateTime.Now;
        public DateTime QueryStartTime
        {
            get { return _QueryStartTime; }
            set { _QueryStartTime = value; }
        }
        private DateTime _QueryEndTime = DateTime.Now;
        public DateTime QueryEndTime
        {
            get { return _QueryEndTime; }
            set { _QueryEndTime = value; }
        }
        
        #endregion
        public XElement ToXElement()
        {
            XElement Ret = new XElement("queryresult");
            XElement Elem = new XElement("name");
            Elem.Value = this._QueryName;
            Ret.Add(Elem);
            Elem = new XElement("clientid");
            Elem.Value = _ClientId.ToString();
            Ret.Add(Elem);
            Elem = new XElement("querytext");
            Elem.Value = this._QueryText;
            Ret.Add(Elem);
            Elem = new XElement("starttime");
            Elem.Value = _QueryStartTime.ToString();
            Ret.Add(Elem);
            Elem = new XElement("endtime");
            Elem.Value = _QueryEndTime.ToString();
            Ret.Add(Elem);
            Elem = new XElement("duration");
            Elem.Value = _Duration.ToString();
            Ret.Add(Elem);
            Elem = new XElement("status");
            int i = (int)this.ExecuteStatus;
            Elem.Value = i.ToString();
            Ret.Add(Elem);
            Elem = new XElement("errorid");
            Elem.Value = _ErrorId.ToString();
            Ret.Add(Elem);
            Elem = new XElement("errormessage");
            Elem.Value = _ErrorMessage;
            Ret.Add(Elem);
            return Ret;

        }
        public override string ToString()
        {
            XElement ret = this.ToXElement();
            return ret.ToString();
        }

        public void LoadFromXMLString(string XML)
        {
            XDocument Doc = XDocument.Parse(XML);
            XElement Elem = Doc.Element("queryresult");
            _QueryName = Elem.Element("name").Value;
            _QueryText = Elem.Element("querytext").Value;
            _QueryStartTime = DateTime.Parse(Elem.Element("starttime").Value);
            _QueryEndTime = DateTime.Parse(Elem.Element("endtime").Value);
            _Duration = Double.Parse(Elem.Element("duration").Value);
            _ErrorId = Int32.Parse(Elem.Element("errorid").Value);
            _ErrorMessage = Elem.Element("errormessage").Value;
            int status = Int32.Parse(Elem.Element("status").Value);
            _ExecuteStatus = (QueryExecuteStatus)status;
           
        }
    }
    public class Test
    {
        private DateTime _Started;
        public DateTime Started
        {
            get { return _Started; }
            set { _Started = value; }
        }
        private DateTime _Ended;
        public DateTime Ended
        {
            get { return _Ended; }
            set { _Ended = value; }
        }

        private int _TestThreadCount = 1;
        public int TestThreadCount
        {
            get
            {
                return _TestThreadCount;
            }
            set
            {
                _TestThreadCount = value;
            }
        }
        private bool _ClearCache = true;

        public bool DoClearCache
        {
            get { return _ClearCache; }
            set { _ClearCache = value; }
        }

        private string _LogDelimiter = "|";

        public string LogDelimiter
        {
            get { return _LogDelimiter; }
            set { _LogDelimiter = value; }
        }
        private bool _AppendToOutput = true;

        public bool AppendToOutput
        {
            get { return _AppendToOutput; }
            set { _AppendToOutput = value; }
        }

        private string _OutputPath = "";

        public string OutputFileName
        {
            get { return System.IO.Path.GetFileName(_OutputPath); }
        }
        public string OutputPath
        {
            get { return _OutputPath; }
            set { _OutputPath = value;}
        }
        private QuerySource _QuerySource;
        private string _FilePath = "";
        public string FilePath
        {
            get { return _FilePath; }
            set { _FilePath = value; }
        }
        
        public QuerySource QuerySource
        {
            get { return _QuerySource; }
            set { _QuerySource = value; }
        }

        private XDocument _TestConfig;
        public int TotalNumberOfQueries
        {
            get 
            {
                return _TimesToRepeat * _Queries.Count * _TestThreadCount;           
            }
        }

        private string _Name = "";


        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }
        private int _TimesToRepeat = 1;

        public int TimesToRepeat
        {
            get { return _TimesToRepeat; }
            set { _TimesToRepeat = value;}
        }
        private int _MSDelayBetweenQueriesFrom = 1000;

        public int MSDelayBetweenQueriesFrom
        {
            get { return _MSDelayBetweenQueriesFrom; }
            set { _MSDelayBetweenQueriesFrom = value; }
        }

        private int _MSDelayBetweenQueriesTo = 5000;
        public int MSDelayBetweenQueriesTo
        {
            get { return _MSDelayBetweenQueriesTo; }
            set { _MSDelayBetweenQueriesTo = value; }
        }

        private QueryPickMode _QueryPickMethod = QueryPickMode.Sequential;
        public QueryPickMode QueryPickMethod
        {
            get { return _QueryPickMethod; }
            set { _QueryPickMethod = value; }
        }
        private Connection _Connection;

        public Connection Connection
        {
            get { return _Connection; }
            set { _Connection = value; }
        }
        private List<Query> _Queries;

        public List<Query> Queries
        {
            get { return _Queries; }
            set { _Queries = value; }
        }


        public Test()
        {
            _Connection = new Connection();
            _QuerySource = new QuerySourceNative(this);
            this.Queries = new List<Query>();
        }
  
        public void LoadFromFile(string Path)
        {
            this.FilePath = Path;
            try
            {
                _TestConfig = XDocument.Load(Path);
            }
            catch (Exception)
            {
                throw;
            }
           
            LoadFromXDocument(_TestConfig);
        }

        public void LoadQueries()
        {
            _Queries = _QuerySource.GetQueries();
        }
        public void LoadFromTraceFile(string Path, string Server, string Database, ConnectionType ConnectionType, bool UniqueQueriesOnly = true)
        {
            FileInfo fi = new FileInfo(Path);
            TraceFile File = new TraceFile();
            File.InitializeAsReader(Path);
            int i = 1;
            _Queries = new List<Query>();
            _Name = fi.Name;
            _Connection = new Connection();
            _Connection.ConnectionType = ConnectionType.SSAS;
            _Connection.Server = Server;
            _Connection.Database = Database;
            _Connection.ConnectionType = ConnectionType;

            int IndexDbName= -1;
            bool AddMe = false;
            for (int k = 0; k <= File.FieldCount; k++)
            {
                switch (File.GetName(k).ToUpper())
                {
                    case "DATABASENAME":
                        IndexDbName = k;
                        break;
                    default:
                        break;
                }
            }
            while (File.Read())
            {
                
                AddMe = false;
                if (File.GetString(0) == "Query End")
                {
                    //We have database name in trace
                    if (IndexDbName > 0)
                    {
                        if (_Connection.Database == File.GetString(IndexDbName).ToUpper())
                        {
                            AddMe = true;

                        }
                    }
                    else
                    {
                        AddMe = true;
                    }
                    if (AddMe)
                    {
                        Query Q = new Query();
                        Q.Text = File.GetString(2);
                        Q.Name = "Tracefile query " + i.ToString();
                        _Queries.Add(Q);
                    }
                }
                i++;
            }
            File.Close();
        }
        public void SaveToFile(string FilePath)
        {
            XDocument Doc = this.ToXDocument();
            Doc.Save(FilePath);
        }

        public XDocument ToXDocument()
        {
            XDocument Ret = new XDocument();
            XElement Root = new XElement("test");
            XElement Elem = new XElement("name");
            Elem.Value = _Name;
            Root.Add(Elem);
            Elem = new XElement("timestorepeat");
            Elem.Value = _TimesToRepeat.ToString();
            Root.Add(Elem);
            XElement Time = new XElement("timebetweenqueries");
            Root.Add(Time);
            Elem = new XElement("from");
            Elem.Value = _MSDelayBetweenQueriesFrom.ToString();
            Time.Add(Elem);
            Elem = new XElement("to");
            Elem.Value = _MSDelayBetweenQueriesTo.ToString();
            Time.Add(Elem);
            Elem = new XElement("testthreadcount");
            Elem.Value = _TestThreadCount.ToString();
            Root.Add(Elem);
            Elem = new XElement("querypickmode");
            int i = (int)_QueryPickMethod;
            Elem.Value = i.ToString();
            Root.Add(Elem);
            Root.Add(_Connection.ToXElement());
            XElement Output = new XElement("output");
            Root.Add(Output);
            Elem = new XElement("path");
            Elem.Value = _OutputPath;
            Output.Add(Elem);
            Elem = new XElement("append");
            int Overwrite = _AppendToOutput ? 1 : 0;
            Elem.Value = Overwrite.ToString();
            Output.Add(Elem);
            Elem = new XElement("clearcache");
            int ClearCache = _ClearCache ? 1 : 0;
            Elem.Value = ClearCache.ToString();
            Root.Add(Elem);
            Elem = new XElement("columndelimiter");
            Elem.Value = _LogDelimiter;
            Root.Add(QuerySource.ToXElement());
            Output.Add(Elem);
            Root.Add(Output);
            Root.Add( new XElement("totalnumberofqueries") {Value = this.TotalNumberOfQueries.ToString()});

            Elem = new XElement("queries");
            Elem.Add(new XAttribute(XName.Get("count"), _Queries.Count.ToString()));
            foreach(Query Q in _Queries) 
            {
               Elem.Add(Q.ToXElement());
            }
            Root.Add(Elem);
            Ret.Add(Root);
            return Ret;
        }
        public bool ClearCache()
        {
            AdomdConnection Con = new AdomdConnection();
            Con.ConnectionString = "Provider=MSOLAP;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=" + this.Connection.Database + ";Data Source=" + this.Connection.Server;
            AdomdCommand Cmd = new AdomdCommand();
            string XMLA = "<Batch xmlns='http://schemas.microsoft.com/analysisservices/2003/engine'>" + 
                           "<ClearCache>" + 
                               "<Object>" + 
                               "<DatabaseID>" + this.Connection.Database + "</DatabaseID>" + 
                               "</Object>" + 
                               "</ClearCache>" + 
                               "</Batch>";
            Cmd.CommandText = XMLA;
            Cmd.Connection = Con;
            try
            {
                Con.Open();
            }
            catch (Exception)
            {
                return false;
            }
            try
            {
                XmlReader Read = Cmd.ExecuteXmlReader();
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }
        public void LoadFromXDocument(XDocument Document)
        {
        
            try
            {
                this.Name = Document.Element("test").Element("name").Value;
                this.QueryPickMethod = (QueryPickMode)Int32.Parse(Document.Element("test").Element("querypickmode").Value);
                this.TimesToRepeat = Int32.Parse(Document.Element("test").Element("timestorepeat").Value);
                this.MSDelayBetweenQueriesFrom = Int32.Parse(Document.Element("test").Element("timebetweenqueries").Element("from").Value);
                this.MSDelayBetweenQueriesTo = Int32.Parse(Document.Element("test").Element("timebetweenqueries").Element("to").Value);
                this.DoClearCache = Int32.Parse(Document.Element("test").Element("clearcache").Value) == 0 ? false : true;
                this.TestThreadCount = Int32.Parse(Document.Element("test").Element("testthreadcount").Value);
                
                ConnectionType type = (ConnectionType)Int32.Parse(Document.Element("test").Element("connection").Element("connectiontype").Value);
                switch (type)
                {
                    case ConnectionType.SSAS:
                        SSASConnection Con = new SSASConnection();
                        this.Connection = Con;
                        break;
                    default:
                        SSASConnection Con2 = new SSASConnection();
                        this.Connection = Con2;
                        break;
                }
                
                _Connection.LoadFromXelement(Document.Element("test").Element("connection"));             
                this.OutputPath = Document.Element("test").Element("output").Element("path").Value;
                this.AppendToOutput = Int32.Parse(Document.Element("test").Element("output").Element("append").Value)==0?false:true;
                
                this.LogDelimiter = Document.Element("test").Element("output").Element("columndelimiter").Value;
                string QueryType = Document.Element("test").Element("querysource").Element("type").Value;
                if (QueryType == "trace")
                {
                    this.QuerySource = new QuerySourceTraceFile(this);
                }
                else
                {
                    this.QuerySource = new QuerySourceNative(this);
                }
                _QuerySource.FromXElement(Document.Element("test").Element("querysource"));
                _Queries = this.QuerySource.GetQueries(Document.Element("test").Element("queries"));
                
            }
            catch (Exception e)
            {
                throw;
            }
        }

    }

    public class Query
    {
        private Guid _Id;

        public Guid Id
        {
            get { return _Id; }
        }

        public Query()
        {
            _Id = Guid.NewGuid();
        }

        public string Hash
        {
            get 
            {
                
                HashAlgorithm Algo = MD5.Create();
                byte[] ByteHash = Algo.ComputeHash(Encoding.UTF8.GetBytes(this.Text));
                StringBuilder SB = new StringBuilder();
                foreach (byte b in ByteHash)
                {
                    SB.Append(b.ToString("X2"));
                }
                return SB.ToString();
            
            }
           
        }
       
        private string _Name;
        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }
        private string _Text;

        public string Text
        {
            get 
            {
                string ret = System.Text.RegularExpressions.Regex.Replace(_Text, @"\r\n?|\n", " ");
                ret = System.Text.RegularExpressions.Regex.Replace(ret, @"\s{2,}", " ");
                return  ret; 
            }
            set 
            {
                _Text = value;
                
            }
        }

        public string RawText
        {
            get { return _Text; }
            set { _Text = value; }
        }



        public XElement ToXElement()
        {
            XElement Ret = new XElement("query");
            XAttribute Id = new XAttribute("id", this.Id.ToString());
            Ret.Add(Id);
            XElement Elem = new XElement("name");
            Elem.Value = _Name;
            Ret.Add(Elem);
            Elem = new XElement("querytext");
            Elem.Value = _Text;
            Ret.Add(Elem);
            return Ret;
        }

        public void FromXelement(XElement Elem)
        {
            _Id = Guid.Parse(Elem.Attribute("id").Value);
            this.Name = Elem.Element("name").Value;
            this.RawText = Elem.Element("querytext").Value;
        }
    }
    public class SSASConnection:Connection,IDisposable
    {
        private AdomdConnection _Con;
        public override string ConnectionString
        {
            get
            {
                return "Provider=MSOLAP;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=" + this.Database + ";Data Source=" + this.Server;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
        protected virtual void Dispose(bool dispose)
        {
            _Con.Close();
        }
 
        public override QueryResult ExecuteQuery(Query Query)
        {
            QueryResult Ret = new QueryResult();
            
            Ret.QueryName = Query.Name;
            Ret.QueryText = Query.Text;
            AdomdCommand Cmd = new AdomdCommand();
            Cmd.CommandText = Query.Text;
            Cmd.Connection = _Con;
            DateTime Start = DateTime.Now;
            try
            {

                CellSet Set = Cmd.ExecuteCellSet();
                Ret.ExecuteStatus = QueryExecuteStatus.QuerySucceeded;

            }
            catch (Exception e)
            {
                Ret.ExecuteStatus = QueryExecuteStatus.QueryFailed;
                Ret.ErrorMessage = e.Message;
            }
            finally
            {
                TimeSpan Duration = DateTime.Now - Start;
                Ret.Duration = Duration.TotalMilliseconds;
            }
            return Ret;
            
        }
        public override bool TestConnection()
        {
            bool ret = false;
            AdomdConnection Con = new AdomdConnection();         
            Con.ConnectionString = this.ConnectionString;
            try
            {
                Con.Open();
                Con.Close();
                ret = true;
            }
            catch (Exception)
            {
                ret = false;
                //throw;
            }
            return ret;
        }
        public override bool Disconnect()
        {
            try
            {
                _Con.Close();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        public override bool Connect()
        {
            bool ret = false;
            _Con = new AdomdConnection();
            _Con.ConnectionString = this.ConnectionString;
            try
            {
                _Con.Open();
                ret = true;
            }
            catch (Exception e)
            {
                ret = false;
            }
            return ret;
        }
    }
}
